package org.shoukaiseki.common.uniqueid;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.shoukaiseki.common.utils.Base62Decimal;
import org.shoukaiseki.common.utils.ByteIntegerUtils;
import org.shoukaiseki.common.utils.StringUtils;

import java.util.Calendar;

/**
 *
 * com.hkedou.common.adsf <br>
 * @author 蒋カイセキ    Japan-Tokyo  2018-08-17 19:45:08<br>
 * ブログ http://shoukaiseki.blog.163.com/<br>
 * E-メール jiang28555@Gmail.com<br>
 **/
public class UniqueIdFactory {


    long nanoTimeTemp=-1;

    long secondTimeTemp =-1;


    /**
     * 时间戳10进制表示 20180831152041198143478644590
     * 年4位,月2位,日2位,时2位,分2位,秒2位,纳秒 9位
     */
    public static final int TYPE_NUMBER=1;

    /**
     * 时间戳62进制表示,例如:WY8VIKr0GpcSU0
     * 年2位,月1位,日1位,时1位,分1位,秒1位,纳秒 6位
     */
    public static final int TYPE_BASE62=0;

    int type= UniqueIdFactory.TYPE_NUMBER;

    int numsize=29;

    /**
     * 机器码/主机编号/服务编号
     * 同样的服务在多个主机,多个服务运行,如果生成的纳秒级时间搓相等,也可通过服务编号保证唯一性
     */
    String machinecode;
    /**
     * 订单前缀
     */
    String prefix;

    byte[] prefixByte;

    byte[] machinecodeByte;

    public UniqueIdFactory(String prefix, String machinecode){
        init(prefix,machinecode);
    }

    public UniqueIdFactory(String prefix, String machinecode, int type){
        this.type=type;
        init(prefix,machinecode);
    }

    private void init(String prefix,String machinecode){
        switch (type){
            case UniqueIdFactory.TYPE_BASE62:
                numsize=13;
                break;
            case UniqueIdFactory.TYPE_NUMBER:
                numsize=29;
                break;
            default:
                numsize=13;
                break;

        }

        if(StringUtils.isBlank(prefix)){
            this.prefix="";
        }else{
            this.prefix=prefix;
            this.numsize+=prefix.length();
            prefixByte=prefix.getBytes();
        }
        if(StringUtils.isBlank(machinecode)){
            this.machinecode=null;
        }else{
            this.machinecode=machinecode;
            this.numsize+=machinecode.length();
            machinecodeByte=machinecode.getBytes();
        }

    }


    public String createNewUniqueid(){
        switch (type){
            case UniqueIdFactory.TYPE_NUMBER:
                return createNewUniqueidNumber();

            case UniqueIdFactory.TYPE_BASE62:
                return createNewUniqueidFast();

            default:
                return createNewUniqueidFast();
        }
    }


    /**
     * 20180817195037666222222
     * 年月日时分秒纳秒级
     * 可能存在位数不对,纳秒级前面如果为0
     * @return
     */
    private String createNewUniqueidFast(){

        Long[] dateMillisNanoTime = dateMillisNanoTime();
        Long millisTime = dateMillisNanoTime[0];
        Long nanoTime = dateMillisNanoTime[1];

        Calendar cal= Calendar.getInstance();
        cal.setTimeInMillis(millisTime);

        byte[] bytes=new byte[numsize];
        int index=0;
        if(prefixByte!=null){
            for (int i = 0; i < prefixByte.length; i++) {
                bytes[index++]=prefixByte[i];
            }
        }

        byte[]  bytesTemp;

        bytesTemp= Base62Decimal.convertDecimalToBase62(cal.get(Calendar.YEAR));
        //2位年份最大可以表示到3843年
        index= ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);


        bytesTemp=Base62Decimal.convertDecimalToBase62(cal.get(Calendar.MONTH)+1);
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);

        bytesTemp=Base62Decimal.convertDecimalToBase62(cal.get(Calendar.DATE));
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);

        bytesTemp=Base62Decimal.convertDecimalToBase62(cal.get(Calendar.HOUR_OF_DAY));
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);

        bytesTemp=Base62Decimal.convertDecimalToBase62(cal.get(Calendar.MINUTE));
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);

        bytesTemp=Base62Decimal.convertDecimalToBase62(cal.get(Calendar.SECOND));
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,0,false);


        bytesTemp = Base62Decimal.convertDecimalToBase62(nanoTime);

        //二进制码至少为6位
        index=ByteIntegerUtils.insertBytes(bytes,index,bytesTemp,6,true);


        index=ByteIntegerUtils.insertBytes(bytes,index,machinecodeByte,0,false);
        return new String(bytes).trim();
    }

    /**
     * 20180817195037666222222
     * 年月日时分秒纳秒级
     * @return
     */
    private String createNewUniqueidNumber(){

        Long[] dateMillisNanoTime = dateMillisNanoTime();
        Long millisTime = dateMillisNanoTime[0]*1000;
        Long dateNanoTime = dateMillisNanoTime[1];


        StringBuffer sb=new StringBuffer();
        sb.append(prefix);
//        sb.append(DateFormatUtils.format(System.currentTimeMillis(),"yyMMddHHmmss"));
//        sb.append(dateNanoTime()).append(machinecode);
        String yyyyMMddHHmmss = DateFormatUtils.format(millisTime, "yyyyMMddHHmmss");
//        String year = yyyyMMddHHmmss.substring(0, 4);
//        String month = yyyyMMddHHmmss.substring(4, 6);
//        String day = yyyyMMddHHmmss.substring(6, 8);
//        String hour = yyyyMMddHHmmss.substring(8, 10);
//        String minute = yyyyMMddHHmmss.substring(10, 12);
//        String second = yyyyMMddHHmmss.substring(12);
//        System.out.println("year="+year);
//        System.out.println("month="+month);
//        System.out.println("day="+day);
//        System.out.println("hour="+hour);
//        System.out.println("minute="+minute);
//        System.out.println("second="+second);
        sb.append(yyyyMMddHHmmss);
        sb.append(String.format("%09d",dateNanoTime));
        sb.append(machinecode);
        return sb.toString().trim();
    }

    public  synchronized Long[] dateMillisNanoTime(){
        long nanoTime =0;


        /**
         * 秒
         */
        long secondTime=0;


        while(true){
            //秒
            secondTime = System.currentTimeMillis()/1000;
            if(Long.compare(secondTimeTemp,secondTime)==1){
                //如果之前生成的秒数小于当前新编码的秒数,则重新获取时间秒
                continue;
            }
            //毫秒,微秒,纳秒
            nanoTime=System.nanoTime()%1000000000;
            if(Long.compare(secondTimeTemp,secondTime)==-1){
                //生成序列时间秒数大于上次生成的时间
                //退出循环
                break;
            }
            //生成序列时间秒数跟上次生成的时间秒数相同,比较纳秒级
            if(Long.compare(nanoTime,nanoTimeTemp)==1){
                //纳秒级大于上次生成的纳秒级,可用
                //退出循环
                break;
            }
            //重新获取秒级时间和纳秒级时间
        }


        nanoTimeTemp=nanoTime;
        secondTimeTemp = secondTime;

        return new Long[]{ secondTime,nanoTime};
    }

}
